/*
*  This file is part of ygg-brute
*  Copyright (c) 2020 ygg-brute authors
*  See LICENSE for licensing information
*/

#define BN256_SIZE 32

typedef uint8_t Bn256[32];

#define bn_from_bytes(a, bytes)              \
    do                                       \
    {                                        \
        for (int i = 0; i < BN256_SIZE; ++i) \
        {                                    \
            (a)[i] = (bytes)[i];             \
        }                                    \
    } while (0)

#define bn_copy(X, Y) bn_from_bytes(X, Y)

#define bn_zero(X)                                \
    do                                            \
    {                                             \
        for (unsigned i = 0; i < BN256_SIZE; ++i) \
            (X)[i] = 0;                           \
    } while (0)

#define bn_add(Z, X, Y)                                             \
    do                                                              \
    {                                                               \
        uint16_t c = 0;                                             \
        for (int i = 0; i < 32; ++i)                                \
        {                                                           \
            c = (uint16_t)((X)[i]) + (uint16_t)((Y)[i]) + (c >> 8); \
            (Z)[i] = c;                                             \
        }                                                           \
    } while (0)

#define bn_sub(Z, X, Y)                                    \
    do                                                     \
    {                                                      \
        uint16_t c = 0;                                    \
        for (int i = 0; i < 32; ++i)                       \
        {                                                  \
            c = (uint16_t)((X)[i]) - ((Y)[i] + (c >> 15)); \
            (Z)[i] = c;                                    \
        }                                                  \
    } while (0)

DECLSPEC inline bool bn_gt(const Bn256 x, const Bn256 y)
{
    for(int i = 31; i >= 0; --i) {
        if(x[i] < y[i]) return false;
        if(x[i] > y[i]) return true;
    }

    return false;
}

DECLSPEC inline bool bn_lt(const Bn256 x, const Bn256 y)
{
    for(int i = 31; i >= 0; --i) {
        if(x[i] > y[i]) return false;
        if(x[i] < y[i]) return true;
    }

    return false;
}

DECLSPEC inline bool bn_eq(const Bn256 x, const Bn256 y)
{
    for(int i = 31; i >= 0; --i) {
        if(x[i] != y[i]) return false;
    }

    return true;
}

DECLSPEC inline void bn_and(Bn256 z, const Bn256 x, const Bn256 y)
{
    for(int i = 31; i >= 0; --i)
        z[i] = x[i] & y[i];
}

DECLSPEC inline uint8_t bn_clz(const Bn256 n)
{
    uint8_t c = 0;
    for(int i = 31; i >= 0; --i) {
        uint8_t l = n[i];
        c += 8;
        if(l) {
            while(l) {
                --c;
                l >>= 1;
            }
            return c;
        }
    }

    return c;
}

DECLSPEC void inline bn_clear_leading_bits(Bn256 a, uint8_t n)
{
    size_t i = 0;
    for(;i < n / 8; ++i) {
        a[31 - i] = 0;
    }

    a[31 - i] &= ((uint32_t)(0xff)) >> (n % 8);
}